package emitter

import (
	"bytes"
	"mi/compiler/scanner"
)

type TextWriter interface {
	Line() int
	Column() int
	Indent() int
	TextPos() int

	RawWrite(s string)
	Write(p []byte)
	WriteString(s string)
	WriteLine()
	WriteLiteral(s string)

	IncreaseIndent()
	DecreaseIndent()

	String() string
	Bytes() []byte
}

type textWriter struct {
	newLine     string
	output      bytes.Buffer
	indent      int
	lineStart   bool
	lineCount   int
	linePos     int
}

func newTextWriter() TextWriter {
	return &textWriter{
		newLine:     "\n",
		indent:      0,
		lineStart:   true,
		lineCount:   0,
		linePos:     0,
	}
}

var indentStrings = []string{"", "    "}

func getIndentString(level int) string {
	if level >= len(indentStrings) {
		indentStrings = append(indentStrings, getIndentString(level-1) + indentStrings[1])
	}
	return indentStrings[level]
}

func getIndentSize() int {
	return len(indentStrings[1])
}

func (w *textWriter) Write(p []byte) {
	if len(p) > 0 {
		if w.lineStart {
			w.output.WriteString(getIndentString(w.indent))
			w.lineStart = false
		}
		w.output.Write(p)
	}
}

func (w *textWriter) WriteString(s string) {
	if len(s) > 0 {
		if w.lineStart {
			w.output.WriteString(getIndentString(w.indent))
			w.lineStart = false
		}
		w.output.WriteString(s)
	}
}

func (w *textWriter) WriteLine() {
	if !w.lineStart {
		w.output.WriteString(w.newLine)
		w.lineCount++
		w.linePos = len(w.output.String())
		w.lineStart = true
	}
}

func (w *textWriter) IncreaseIndent() {
	w.indent++
}

func (w *textWriter) DecreaseIndent() {
	w.indent--
}

func (w *textWriter) String() string {
	return w.output.String()
}

func (w *textWriter) Bytes() []byte {
	return w.output.Bytes()
}

func (w *textWriter) RawWrite(s string) {
	if len(s) > 0 {
		if w.lineStart {
			w.lineStart = false
		}
		w.output.WriteString(s)
	}
}

func (w *textWriter) WriteLiteral(s string) {
	if len(s) > 0 {
		w.WriteString(s)
		var lineStartsOfS = scanner.ComputeLineStarts([]byte(s))
		if len(lineStartsOfS) > 1 {
			w.lineCount = w.lineCount + len(lineStartsOfS) - 1
			w.linePos = len(w.output.String()) - len(s) + lineStartsOfS[len(lineStartsOfS)-1]
		}
	}
}

func (w *textWriter) TextPos() int {
	return len(w.output.String())
}

func (w *textWriter) Line() int {
	return w.lineCount + 1
}

func (w *textWriter) Column() int {
	if w.lineStart {
		return w.indent * getIndentSize() + 1
	} else {
		return len(w.output.String()) - w.linePos + 1
	}
}

func (w *textWriter) Indent() int {
	return w.indent
}
